package com.fas.hehe.system.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.permission.WildcardPermission;

import com.fas.core.BaseService;
import com.fas.core.constant.R;
import com.fas.core.plugin.jfinal.kit.ModelKit;
import com.fas.core.plugin.tree.jstree.Node;
import com.fas.core.util.sequence.Sequence;
import com.fas.hehe.system.model.Resource;
import com.jfinal.aop.Before;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.tx.Tx;

/**
 * 资源业务处理类 资源包含：菜单、按钮
 *
 * @Copyright: Copyright (c) 2016 FAS
 * @anthor yixiaoping<thinker1013@163.com>
 * @since 2016年1月24日 上午11:35:43
 * @version V1.0
 */
public class ResourceService extends BaseService {
	/**
	 * 
	 * @Title:得到资源对应的权限字符串
	 * @Description: 根据资源ID列表获取对应的权限集合，主要用于Shiro
	 * @param resourceIds
	 *            资源ID列表
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:26:11
	 */
	public Set<String> findPermissions(Set<Long> resourceIds) {
		Set<String> permissions = new HashSet<String>();
		for (Long resourceId : resourceIds) {
			Resource resource = findOne(resourceId);
			if (resource != null && !StringUtils.isEmpty(resource.getPermission())) {
				permissions.add(resource.getPermission());
			}
		}
		return permissions;
	}

	/**
	 * 
	 * @Title:根据用户权限，生成菜单HTML
	 * @Description:
	 * @param parentId
	 *            父节点ID
	 * @param contentPath
	 *            上下文，例如：hehe
	 * @param permissions
	 *            权限集合
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:23:30
	 */
	public String buildMenus(Long parentId, String contentPath, Set<String> permissions) {
		StringBuffer html = new StringBuffer();
		List<Resource> menus = this.findMenusByParentId(parentId);

		if (CollectionUtils.isNotEmpty(menus)) {
			for (Resource res : menus) {

				if (!hasPermission(permissions, res)) {
					continue;
				}

				String menuName = res.getName();
				Long menuId = res.getId();
				String url = res.getUrl();
				String itemClass = res.getIconClass();
				if (StrKit.isBlank(url)) {
					url = "javascript:void(0)";
				} else {
					url = contentPath + url;
				}

				List<Resource> subMenus = this.findMenusByParentId(menuId);
				if (CollectionUtils.isNotEmpty(subMenus)) {
					html.append("<li class='").append("").append("'>");
					html.append("	<a class='dropdown-toggle'>");
					html.append("       <i class='").append(itemClass + "").append("'></i>");// 图标
					html.append("		<span class='menu-text'>").append(menuName).append("</span>");
					html.append("   	<b class='arrow fa fa-angle-down'></b>");
					html.append("   </a>");
					html.append("   <b class='arrow'></b>");

					html.append("	<ul class='submenu'>");

					html.append(buildMenus(menuId, contentPath, permissions));

					html.append("   </ul>");
					html.append("</li>");
				} else {
					html.append("<li class='").append("").append("'>");
					html.append("<a target='main_frame' href='").append(url).append("'>");
					html.append("<i class='").append(itemClass + "").append("'></i>");// 图标
					html.append("	<span class='menu-text'>").append(menuName).append("</span>");
					html.append("</a><b class='arrow'></b>");
					html.append("</li>");
				}
			}
		}
		return html.toString();
	}

	/**
	 * 
	 * @Title:根据父资源ID查询子资源
	 * @Description:
	 * @param parentId
	 *            父资源ID
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:30:30
	 */
	public List<Resource> findMenusByParentId(Long parentId) {
		final String sql = "select * from sys_resource where parent_id = ? and type = ? and available =? order by priority asc";
		return Resource.dao.find(sql, parentId, R.Dict.RES_MENU, R.Dict.STATUS_AVAILABLE);
	}

	/**
	 * 
	 * @Title:根据资源ID查询资源对象
	 * @Description:
	 * @param resourceId
	 *            资源ID
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:31:00
	 */
	public Resource findOne(Long resourceId) {
		return Resource.dao.findById(resourceId);
	}

	/**
	 * 
	 * @Title:加载所有资源
	 * @Description:
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:31:19
	 */
	public List<Resource> findAll() {
		final String sql = "select * from sys_resource where available =? order by concat(parent_ids, id) asc";
		return Resource.dao.find(sql, R.Dict.STATUS_AVAILABLE);
	}

	/**
	 * 
	 * @Title:判断用户是否有资源权限
	 * @Description:
	 * @param permissions
	 *            用户权限集合
	 * @param resource
	 *            资源
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 上午11:31:42
	 */
	private boolean hasPermission(Set<String> permissions, Resource resource) {
		if (StringUtils.isEmpty(resource.getPermission())) {
			return true;
		}
		for (String permission : permissions) {
			WildcardPermission p1 = new WildcardPermission(permission);
			WildcardPermission p2 = new WildcardPermission(resource.getPermission());
			if (p1.implies(p2) || p2.implies(p1)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @Title:loadTreeOnceTime
	 * @Description: 一次性加载所有资源树，构建jstree插件树形结构对象列表
	 * @param roleId
	 *            角色ID 当角色ID为null时，加载所有资源
	 * @return 参数说明
	 * @return List<Node> 返回类型
	 * @throws
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月26日 下午3:19:08
	 */
	public List<Node> loadTreeOnceTime() {

		String sql = "select * from sys_resource where FIND_IN_SET(id, queryChildrenResource(?)) and available =? order by priority asc";
		List<Resource> list = Resource.dao.findByCache(R.CacheName.GLOBAL_RES_DATA, R.Const.ROOT_RESID, sql,
				R.Const.ROOT_RESID, R.Dict.STATUS_AVAILABLE); // 后台根据父节点查询出的数据列表
		HashMap<Long, Resource> resMap = new HashMap<Long, Resource>();
		TreeMap<Long, List<Resource>> subResMap = new TreeMap<Long, List<Resource>>();
		for (Resource res : list) {
			Long id = res.getId();
			Long pid = res.getParentId();

			if (id != R.Const.ROOT_RESID) {
				List<Resource> subList = subResMap.get(pid);
				if (subList == null)
					subList = new ArrayList<Resource>();
				subList.add(res);
				subResMap.put(pid, subList);
			}
			resMap.put(id, res);
		}

		List<Node> nodeList = new ArrayList<Node>();
		Node rootNode = buildTreeNode(R.Const.ROOT_RESID, subResMap, resMap);
		nodeList.add(rootNode);
		return nodeList;
	}

	/**
	 * 
	 * @Title:递归构建树jstree插件树形节点对象
	 * @Description:
	 * @param parentId
	 *            当前节点ID
	 * @param subResMap
	 *            {key: 资源IDLong,VALUE: 子节点列表List}
	 * @param resMap
	 *            {key: 资源IDLong,VALUE: 资源对象}
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月23日 下午2:09:12
	 */
	private Node buildTreeNode(Long parentId, TreeMap<Long, List<Resource>> subResMap, HashMap<Long, Resource> resMap) {
		List<Resource> subList = subResMap.get(parentId);
		Node node = new Node();

		Resource res = resMap.get(parentId);
		Long resId = res.getId();
		String resName = res.getName();
		String resType = res.getType();
		node.setId(resId.toString());
		node.setText(resName);
		node.setType(resType);
		// node.addData("icon_class", res.getIconClass());// 用户自定义数据
		// node.addData("permission", res.getPermission());

		if (CollectionUtils.isNotEmpty(subList)) {
			List<Node> childrenNodes = new ArrayList<Node>();
			for (Resource subRes : subList) {
				Long subResId = subRes.getId();
				Node e = this.buildTreeNode(subResId, subResMap, resMap);
				childrenNodes.add(e);
			}
			node.setChildren(childrenNodes);
		}
		return node;
	}

	/**
	 * 
	 * @Title:根据资源ID查询子列表
	 * @Description:
	 * @param resId
	 *            资源ID
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 下午12:45:53
	 */
	public List<Resource> findSubResourceById(Long resId) {
		String sql = "select * from sys_resource where parent_id = ? and available =? order by priority asc";
		return Resource.dao.find(sql, resId, R.Dict.STATUS_AVAILABLE);
	}

	/**
	 * 
	 * @Title: 修改资源优先级别（显示顺序）
	 * @Description:
	 * @param resId
	 *            资源ID
	 * @param type
	 *            操作类型： up：上移 ，优先级别（显示顺序）加1 down：下移，优先级别（显示顺序）减1
	 * 
	 * @param priority
	 *            优先级别（显示顺序）
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月23日 下午2:13:03
	 */
	@Before(Tx.class)
	public boolean changeResourcePriority(String resId, String type, int priority) {
		int otherPriority = 0;
		final String UP = "up";
		if (UP.equals(type)) {
			otherPriority = priority - 1;
		} else {
			otherPriority = priority + 1;
		}
		String sql = "select * from sys_resource where parent_id = ? and priority = ?";
		Resource curData = Resource.dao.findFirst(sql, resId, priority);
		Resource otherOrg = Resource.dao.findFirst(sql, resId, otherPriority);
		if (curData != null) {
			curData.setPriority(otherPriority);
			curData.update();
		}
		if (otherOrg != null) {
			otherOrg.setPriority(priority);
			otherOrg.update();
		}
		return true;
	}

	/**
	 * 
	 * @Title: 根据ID，删除资源
	 * @Description: 逻辑删除，将状态改为0-不可用；不进行物理删除
	 * @param id
	 *            资源ID
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月23日 下午2:16:46
	 */
	@Before(Tx.class)
	public boolean deleteResourceById(Long id) {
		Resource oldData = Resource.dao.findById(id);
		Resource newData = new Resource();
		ModelKit.clone(oldData, newData);

		oldData.set("available", R.Dict.STATUS_UNAVAILABLE);
		oldData.update();
		this.saveOperateLog(R.OperType.UPDATE, newData, oldData, this.getLoginUser());

		autoResetPriority(oldData.getParentId());
		return true;
	}

	/**
	 * 
	 * @Title:autoResetPriority
	 * @Description: <pre>
	 * 				重新设置机构所有子机构的优先级别 ,通常在删除机构后操作。
	 * 				 1）将子机构中，状态为“0-不可用”的记录优先级别全部更新为-1 
	 * 				 2) 将子机构中，状态为“1-可用”的记录，优先级别重新设置，但不改变原先这些值的前后顺序，只是让Priority值从1开始逐1递增
	 *              
	 * 				例如：   ID   状态     优先级                                   重置后状态为： ID   状态     优先级     
	 *             		101    1    1                     101    1    1
	 *                  102    1    2					  102    1    2
	 *                  103    0    3					  104    1    3
	 *                  104    1    4				      106    1    4
	 *                  105    0    5					  103    0    -1
	 *                  106    1    6					  105    0    -1
	 * </pre>
	 * @param parent_id
	 *            参数说明 父节点ID
	 * @return void 返回类型
	 * @throws
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月28日 下午10:36:39
	 */
	public void autoResetPriority(Long parent_id) {
		// 将状态为"不可用"的记录，优先级全部设置为-1
		String updateSql = "update sys_resource set priority = ? where parent_id = ? and available =?";
		Db.update(updateSql, R.Const.DEFAULT_PRIORITY_VAL, parent_id, R.Dict.STATUS_UNAVAILABLE);

		// 重新设置状态为"可用"记录的优先级别值，但先后顺序不变
		String selectSql = "select * from sys_resource where parent_id =? and available =?";
		List<Resource> list = Resource.dao.find(selectSql, parent_id, R.Dict.STATUS_AVAILABLE);
		ArrayList<Resource> newList = new ArrayList<Resource>();
		for (int i = 0; i < list.size(); i++) {
			Resource res = list.get(i);
			res.setPriority(i + 1);
			newList.add(res);
		}
		list = null;
		Db.batchUpdate(newList, R.Const.DB_BATCH_SIZE);
	}

	/**
	 * 
	 * @Title:根据ID查询子资源中优先级最大的值
	 * @Description:
	 * @param parentId
	 *            资源ID
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 下午9:59:03
	 */
	public int findMaxPrioritybyPid(Long parentId) {
		String sql = "select max(priority)  as priority from sys_resource where parent_id = ? and available =?";
		Resource data = Resource.dao.findFirst(sql, parentId, R.Dict.STATUS_AVAILABLE);
		return data.getPriority() == null ? 0 : data.getPriority();
	}

	/**
	 * 
	 * @Title:保存资源（新增、修改）
	 * @Description:
	 * @param resource
	 *            资源对象
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 下午9:59:35
	 */
	public Resource saveResource(Resource resource) {
		Long id = resource.getId();
		if (id == null) {
			Long pid = resource.getParentId();
			int priority = this.findMaxPrioritybyPid(pid) + 1;

			id = Sequence.getSequence();
			resource.setId(id);
			resource.setAvailable(Integer.valueOf(R.Dict.STATUS_AVAILABLE));
			resource.setPriority(priority);
			resource.save();
		} else {
			Resource oldresource = Resource.dao.findById(id);
			oldresource.setName(resource.getName());
			;
			oldresource.setIconClass(resource.getIconClass());
			oldresource.setPermission(resource.getPermission());
			oldresource.setUrl(resource.getUrl());
			oldresource.update();
		}
		return resource;
	}

	/**
	 * 
	 * @Title:构建JSTREE树形插件节点对象
	 * @Description:
	 * @param resource
	 *            资源对象
	 * @return
	 * @anthor yixiaoping<thinker163@163.com>
	 * @date 2016年1月24日 下午9:59:54
	 */
	public Node buildJstreeNode(Resource resource) {
		Node node = new Node();
		node.setId(resource.getId().toString());
		node.setText(resource.getName());
		node.setType(resource.getType());

		return node;
	}

}
